using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Castle.DynamicProxy;

namespace ebcbinder
{
    public class EbcBinder
    {
        private readonly Stack<long> openedGroups;
        private readonly BindingInterceptor interceptor;


        public EbcBinder()
        {
            Trace.TraceInformation("EbcBinder started, logging to ebcbinder.log.csv");
            this.openedGroups = new Stack<long>();
            this.interceptor = new BindingInterceptor(this.openedGroups);

            File.Delete("ebcbinder.log.csv");
            File.WriteAllLines("ebcbinder.log.csv", new[]{"boardId;outId;outTypeName;outPinName;inId;inTypeName;inPinName"});
        }


        public TContract Create<TContract>(object instance)
        {
            Trace.TraceInformation("EbcBinder.Create<{0}, {1}>", typeof(TContract).Name, instance.GetType().Name);

            var proxyGenerator = new ProxyGenerator();
            return (TContract)proxyGenerator.CreateInterfaceProxyWithTarget(
                                typeof(TContract),
                                new[] { typeof(TContract) },
                                instance,
                                ProxyGenerationOptions.Default,
                                this.interceptor);
        }


        public TContract Create<TContract, TImplementation>() where TImplementation : new()
        {
            return this.Create<TContract>(new TImplementation());
        }


        public EbcBindingGroup BeginRootGroup()
        {
            if (this.openedGroups.Count > 0) throw new InvalidOperationException("Root group can only be opened while not other groups are open!");
            return new EbcBindingGroup(this.openedGroups, null);
        }


        public EbcBindingGroup BeginGroup(object root)
        {
            return new EbcBindingGroup(this.openedGroups, root);
        }
    }


    public class EbcBindingGroup : IDisposable
    {
        private readonly Stack<long> openedGroups;


        internal EbcBindingGroup(Stack<long> openedGroups, object root)
        {
            this.openedGroups = openedGroups;
            this.openedGroups.Push(root == null ? 0L : root.GetHashCode());
            Trace.TraceInformation("EbcBinder.BeginGroup({0})", this.openedGroups.Peek());
        }


        public void Dispose()
        {
            Trace.TraceInformation("EbcBinder.EbcBindingGroup.Dispose: {0}", this.openedGroups.Peek());
            this.openedGroups.Pop();
        }
    }
}